home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-11-07 | 31.9 KB | 1,038 lines | [TEXT/R*ch] |
- // File: LabMaker.c
-
- #undef DEBUG
-
- #include <stdio.h>
- #include <ctype.h>
- #include <string.h>
- #include "LabMaker.h"
-
- // --- Global Variables
- CommandEntry gCommandTable[] = {
- { "comment", 3, DoComment, kPassParams}, // Usage: $comment <comment-delim> [<comment-delim>]
- // LabMaker finds the comment delimiters around the
- // directive and uses these to detect comments in the file.
- { "create", 2, DoCreate, kPassParams}, // Usage: ($create <lab-name> [in <directory-name>])+ [from <lab-name>]
- // Bind a lab name to a directory name, creating the
- // directory if it doesn't exist, and begin writing
- // the current file to the lab directory. If the "from"
- // keyword is specified, copy from the specified lab
- // into the new lab file before adding any lines.
- { "include", 3, DoInclude, kPassParams}, // Usage: $include <file-name>
- // reads in configuration file
- { "insert", 3, DoInsert, kPassBuffer}, // Usage: $insert <lab-name>[ <lab-name>]* data <text to replace this comment with>
- { "copy", 3, DoCopy, kPassParams}, // Usage: $copy <lab-name>[ <lab-name>]*
- // Begins copying lines into the specified labs (including
- // this line if it contains more than just a LabMaker
- // command.
- { "skip", 2, DoSkip, kPassParams}, // Usage: $skip <lab-name>[ <lab-name>]*
- // Stop copying lines (inclusive) into the specified labs.
- { "push", 2, DoPush, kNoParams}, // Usage: $push
- // Remember the current set of active labs.
- { "pop", 2, DoPop, kNoParams}, // Usage: $pop
- // Recall a pushed set of labs
- { "suffix", 1, DoSuffix, kPassParams}, // Usage: $suffix <filename>|<file-suffix>
- // Beginning of a "suffix definition" block which lists
- // the options for a particular type of file.
- { "delimiter", 1, DoDelimiter, kPassParams},// Usage: $delimiter <delim-character>
- // Use at the start of a file to change the command prefix
- // character from "$" to something else
- { "reset", 1, DoReset, kNoParams}, // Usage: $reset
- // Discard the list of labs and reset all parameters
- // to their default settings
- { "only", 1, DoOnly, kPassParams}, // Usage: $only <lab-name>[ <lab-name>]*
- // Only include this line in the specified list of labs.
- { "not", 1, DoNot, kPassParams}, // Usage: $not <lab-name>[ <lab-name>]*
- { "end-suffix", 1, DoEndSuffix, kNoParams}, // Usage: $end-suffix
- // Reset comment and command characters to defaults
- { "skip-blanks", 5, DoSkipBlanks, kNoParams}// Usage: $skip-blanks
- // Ignore blank lines in this file
- };
-
- enum { kCommentCommand = 0 };
-
- short gNumCommands = 15;
-
- short gAutoPop;
- CurrentState gStateStack[kStackSize];
- short gStackPointer;
-
- short gNumFileOptions;
- FileOptions gAllFileOptions[kMaxFileTypes];
- FileOptions gDefaultFileOptions;
- CurrentState gCurrState;
-
- // Compatibility macros for code written before I added gCurrState and Push/Pop
- #define gCurrFilename (gCurrState.currFilename)
- #define gCurrFileOptions (gCurrState.currFileOptions)
- #define gNumLabs (gCurrState.numLabs)
- #define gLabs (gCurrState.labs)
- #define gCurrBuffer (gCurrState.buffer)
-
- static int ProcessFile(char* filename, short isInclude);
-
- // --- Public functions
- ProcessUserFile(char *filename)
- {
- int result;
-
- DoReset(nil, 0, 0, nil);
- GetFileOptions(filename);
- result = ProcessFile(filename, 0);
- CloseAllLabs();
- return result;
- }
-
- static int ProcessFile(char* filename, short isInclude)
- // Internal routine for reading in a file and processing it
- {
- FILE *f = nil;
- char buffer[kMaxLineLength];
- int inComment = 0;
-
- gCurrFilename = filename;
- gCurrState.isInclude = isInclude;
- f = fopen(filename, "r");
- if (f != nil) {
- fgets(buffer, kMaxLineLength, f);
- for (; feof(f) == 0; fgets(buffer, kMaxLineLength, f)) {
- short length = strlen(buffer);
- if (buffer[length - 1] == '\n') {
- if (gCurrState.skipBlanks)
- continue; // Ignore blank lines
- else
- buffer[length - 1] = ' '; // Replace the newline with a space to simplify later processing
- }
- ProcessLine(buffer, &inComment, filename);
- }
- fclose(f);
- return 0;
- }
- return -1;
- }
-
-
- void ProcessLine(char *buffer, int *inComment, char* filename)
- // Take one line from the file and process it. If the line contains a comment with a command,
- // execute the command and delete the comment.
- {
- char *commentStart;
- short index;
-
- // Start at the beginning of the buffer and find any comment string
- char result[kMaxLineLength];
- short startOfComment, endOfComment;
- short commentKind;
-
- strcpy(gCurrBuffer, buffer);
-
- if (gAutoPop) { // Set by commands that only operate on a single line --
- Pop(kReplaceLabs); // they call Push before changing the active labs list,
- gAutoPop = 0; // and expect a Pop to happen before the next line.
- }
-
- #ifdef DEBUG
- printf("Processing: %s\n", buffer);
- #endif
-
- index = 0;
- for (;;) {
- // If we're not in a comment, look for the start of one
- if (!*inComment &&
- ((commentStart = FindCommentStart(buffer, &index, &startOfComment, &commentKind)) != NULL))
- *inComment = 1;
-
- if (!*inComment) {
- break; // Go ahead and distribute the line
- } else {
- // Look for a command
- CommandEntry *ce = NULL;
-
- // N.B. We only support single commands placed at the start of each comment
- if ((GetNextSubstring(buffer, &index, result, 0) != NULL) &&
- ((ce = FindCommand(result)) != NULL)) {
- char extractedComment[kMaxLineLength] = {'\0'};
- short numChars, endOfCommand;
- short paramStart, paramEnd;
-
- // Locate the end of the command and the start of the parameters
- endOfCommand = index - 1;
- SkipWhitespace(buffer, &index);
- paramStart = index;
-
- // Locate the end of the comment. After this, index will refer to
- // one place past the end of the comment and endOfComment refers
- // to the last character inside the comment
- if (!FindCommentEnd(buffer, &index, &endOfComment, commentKind)) {
- Warning("Commands found in unterminated comments are not executed", buffer, startOfComment, filename);
- break; // Unterminated comment -- go ahead and distribute the line
- }
-
- // See what the command wants: no parameters, the whole comment, or the
- // parameter list only
- if (ce->options == kPassBuffer) {
- // Pass the whole buffer, which may be modified by the command
- paramStart = startOfComment; // We have to know where the comment starts so we can remove it
- paramEnd = index;
- ce->theCommand(buffer, paramStart, paramEnd, filename);
- // The command is responsible for removing the comment from the buffer,
- // so recycle to the start of the comment and process whatever remains
- index = startOfComment;
- *inComment = 0;
- continue;
- } else if (ce->options == kNoParams) {
- paramStart = paramEnd = 0; // Call the command without parameters
- }
- else if (ce->options == kPassComment) {
- // Copy out the section of the buffer in [startOfComment, index)
- // which will give us the whole comment.
- numChars = index - startOfComment;
- strncpy(extractedComment, buffer + startOfComment, numChars);
- extractedComment[numChars] = '\0'; // strncpy doesn't always add a trailing null
- // Create references to the start and the end of the parameter list
- paramStart -= startOfComment;
- paramEnd = endOfComment - startOfComment;
- }
- else if (ce->options == kPassParams) {
- // Copy out the section of the buffer between endOfCommand and endOfComment
- // which will give us only the parameters.
- numChars = endOfComment - paramStart + 1;
- strncpy(extractedComment, buffer + paramStart, numChars);
- extractedComment[numChars] = '\0'; // strncpy doesn't always add a trailing null
- // Create references to the start and the end of the parameter list
- #ifdef DEBUG
- printf("Extracted “%s”\n", extractedComment);
- #endif
- paramStart = 0;
- paramEnd = numChars;
- }
-
- // Execute the command
- ce->theCommand(extractedComment, paramStart, paramEnd, filename);
-
- // Remove the entire comment from the buffer
- *inComment = 0;
- RemoveComment(buffer, &index, startOfComment);
- } else {
- // No command was found in this comment, so find the end
- #ifdef DEBUG
- printf("*No command found\n");
- #endif
- if (FindCommentEnd(buffer, &index, &endOfComment, commentKind)) {
- *inComment = 0;
- #ifdef DEBUG
- printf("*Found end of comment\n");
- #endif
- } else
- break; // Unterminated comment -- go ahead and distribute the line
- }
- }
- }
-
- DistributeLine(buffer); // Copy this line to the active labs
- }
-
-
- void RemoveComment(char *buffer, short *index, short startOfComment)
- // Delete a comment from the buffer. Index is as returned by FindCommentEnd
- // (i.e. one past the end delimiter); startOfComment is as returned by
- // FondCommentStart.
- {
- short numChars, newLen;
- numChars = strlen(buffer) - *index;
- memmove(&buffer[startOfComment], &buffer[*index], numChars);
- newLen = startOfComment + numChars;
- buffer[newLen] = '\0'; // memmove won't null terminate the new string, so we will
- *index = startOfComment;
- }
-
-
- CommandEntry *FindCommand(char *possibleCommand)
- // We have what could be the start of a command
- // so check our command table for a matching entry
- {
- short commandNum;
-
- MakeLower(possibleCommand); // Move to lowercase so we aren't case sensitive
-
- #ifdef DEBUG
- printf("Looking for command in: “%s”\n", possibleCommand);
- #endif
-
- if (*possibleCommand == gCurrFileOptions->commandChar)
- possibleCommand++;
-
- for (commandNum = 0; commandNum < gNumCommands; commandNum++) {
- CommandEntry *currCommand = &gCommandTable[commandNum];
- if (strncmp(possibleCommand, currCommand->commandName, currCommand->uniqueChars) == 0) {
- #ifdef DEBUG
- printf("Found command: %s\n", possibleCommand);
- #endif
- return currCommand;
- }
- }
-
- return NULL;
- }
-
-
- short FindLab(char* labName)
- // Returns kLabNotFound (-1) if the lab couldn't be found
- // Otherwise returns kAllLabs (-2) or the index number of the
- // lab in the gLabs table.
- {
- short labNum;
- char localLabName[kMaxLabNameLength],
- globalLabName[kMaxLabNameLength];
-
- #ifdef DEBUG
- printf("Looking for the lab named: %s\n", labName);
- #endif
-
- strcpy(localLabName, labName);
- MakeLower(localLabName);
-
- if (strcmp(localLabName, "all") == 0)
- return kAllLabs;
-
- for (labNum = 0; labNum < gNumLabs; labNum++) {
- // Make a local lowercase copy of the lab name so we don't penalize
- // the user for typing case errors
- strcpy(globalLabName, gLabs[labNum].labName);
- MakeLower(globalLabName);
- if (strcmp(localLabName, globalLabName) == 0)
- return labNum;
- }
-
- return kLabNotFound;
- }
-
-
- short FindSuffix(char* filename)
- // Returns kSuffixNotFound (-1) if the suffix couldn't be found
- // Otherwise returns the index number of the suffix in gAllFileOptions.
- {
- short index;
- char localSuffix[kMaxLabNameLength];
- char *found;
-
- if (gNumFileOptions > 0) {
- MakeLower(filename);
- for (index = 0; index < gNumFileOptions; index++) {
- // Make a local lowercase copy of the suffix so we don't penalize
- // the user for typing case errors
- strcpy(localSuffix, gAllFileOptions[index].suffix);
- MakeLower(localSuffix);
- if ((found = strstr(filename, localSuffix)) != NULL) {
- // Confirm that the suffix is at the end of the string
- if (strlen(localSuffix) == strlen(found))
- return index;
- }
- }
- }
- return kSuffixNotFound;
- }
-
-
- char *FindCommentStart(char *buffer, short *index, short *startOffset, short *commentKind)
- // Locate the beginning of a comment and let us know where it starts (startOffset) and set
- // index to the character after the comment delimiter
- {
- char *startPoint = &buffer[*index];
- char *foundPoint = NULL;
- short comType; // Index into the table of comment types
-
- // To see if we can find the start of a comment, do the following:
- // 1. Scan the comment string for the first character of each comment delimiter
- // 2. If the first character is found, test the rest of the characters of
- // the delimiter against the found point.
- for (comType = 0; comType < kMaxCommentTypes; comType++) {
- char *testComment = gCurrFileOptions->commentDelimiters[comType].commentStart;
-
- #ifdef DEBUG
- // printf("%s\n", testComment);
- #endif
- if (*testComment != '\0') {
- foundPoint = strchr(startPoint, *testComment);
- if (foundPoint != NULL) {
- short limit = strlen(testComment);
- if (strncmp(foundPoint, testComment, limit) == 0) {
- // Compute the offsets to the beginning and end of the comment
- *startOffset = (unsigned int)foundPoint - (unsigned int)buffer;
- *index = (unsigned int)*startOffset + (unsigned int)limit;
- *commentKind = comType;
- break; // Jump to "return foundPoint"
- }
- }
- }
- }
- return foundPoint;
- }
-
-
- char *FindCommentEnd(char *buffer, short *index, short *endOffset, short commentKind)
- // Locate the end of a specific comment type. Set endOffset to the last character
- // contained within the comment and set index to the character after the comment delimiter
- {
- char *startPoint = &buffer[*index];
- char *foundPoint = NULL;
-
- // To see if we can find the end of a comment, do the following:
- // 1. Scan the comment string for the first character the comment delimiter
- // 2. If the first character is fiound, test the rest of the characters of
- // the delimiter against the found point.
- char *testComment = gCurrFileOptions->commentDelimiters[commentKind].commentEnd;
-
- if (*testComment == '\0') {
- // This comment goes to the end of the line
- *endOffset = strlen(buffer) - 1;
- *index = strlen(buffer);
- foundPoint = &buffer[*endOffset];
- } else {
- // We have an end delimiter, so search for it
- foundPoint = strstr(startPoint, testComment);
- if (foundPoint != NULL) {
- // Compute the offsets to the beginning and end of the comment delimiter
- *endOffset = (unsigned int)foundPoint - (unsigned int)buffer - 1;
- *index = (unsigned int)*endOffset + (unsigned int)strlen(testComment) + 1;
- }
- }
- return foundPoint;
- }
-
-
- char *SkipWhitespace(char *buffer, short *index)
- // Skip over any spaces or tabs in the buffer beginning at *index
- {
- short localIndex = *index;
- char *currChar = &buffer[localIndex];
- char ch;
-
- ch = *currChar;
- while ((ch != '\0') && (ch == ' ') || (ch == '\t')) {
- currChar++; localIndex++;
- ch = *currChar;
- }
-
- *index = localIndex;
- if (currChar == '\0') {
- return NULL;
- } else {
- return &buffer[localIndex];
- }
- }
-
-
- void DistributeLine(char *buffer)
- // Write the current buffer to this file in all of the active labs
- {
- short labID;
-
- if ((gNumLabs > 0) && (*buffer != '\0')) {
- for (labID = 0; labID < gNumLabs; labID++) {
- if (gLabs[labID].isActive) {
- fputs(buffer, gLabs[labID].file);
- fputc('\n', gLabs[labID].file);
- }
- }
- }
- }
-
-
- static char *MakeLower(char* buffer)
- // Convert the entire string in buffer to lowercase.
- // The buffer is modified
- {
- char *currChar;
- for (currChar = buffer; *currChar != '\0'; currChar++) {
- *currChar = tolower(*currChar);
- }
- return buffer;
- }
-
-
- char *GetKeyword(char* keyword, char* buffer, short *index, char* result)
- // Extract the next token in the buffer and see if it matches the specified
- // keyword
- {
- short indexCopy = *index;
- short startIndex = *index;
-
- if (GetNextSubstring(buffer, &indexCopy, result, 0) != NULL) {
- char *currChar;
- // Convert the string into lowercase for comparison purposes
- for (currChar = result; *currChar != '\0'; currChar++) {
- *currChar = tolower(*currChar);
- }
-
- // Do the strings match?
- if (strcmp(result, keyword) == 0) {
- *index = indexCopy;
- return &buffer[startIndex];
- }
- }
- return NULL; // No matching keyword found
- }
-
-
- char *GetNextSubstring(char* buffer, short *index, char *result, short allowQuotes)
- {
- // Extract a lab name (possibly quoted) from the buffer
- char quote = '\0';
- char *currChar, ch;
- short start;
- short startIndex;
-
- currChar = SkipWhitespace(buffer, index);
- if (currChar == NULL) {
- *result = '\0';
- return NULL;
- };
-
- startIndex = *index;
-
- // Get any leading quote
- if (allowQuotes && (*currChar == '"') || (*currChar == '\'')) {
- quote = *currChar;
- currChar++; *index += 1;
- }
- start = *index;
-
- // Scan to the end of the string
- if (quote != '\0') {
- // If we have a quote, it ends the string
- ch = *currChar;
- while ((ch != '\0') && (ch != quote)) {
- currChar++; *index += 1;
- ch = *currChar;
- }
- } else {
- // No quotation marks, so a space or tab ends the string\
- // If this is a possible lab name, a comma also ends the name.
- ch = *currChar;
- while ((ch != '\0') && (ch != '\n') && (ch != ' ') && (ch != '\t')) {
- currChar++; *index += 1;
- ch = *currChar;
- }
- }
-
- if (start < *index) {
- short numChars = *index - start; // Don't copy the trailing space, thus no "+1"
- strncpy(result, &buffer[start], numChars);
- result[numChars] = '\0'; // Terminate the string
- return &buffer[startIndex]; // Point to the start of the original substring
- } else {
- *result = '\0';
- return NULL;
- }
- }
-
-
- char *GetLabName(char* buffer, short *index, char *result)
- {
- return GetNextSubstring(buffer, index, result, 1);
- }
-
-
- char *GetFileName(char* buffer, short *index, char *result)
- {
- return GetNextSubstring(buffer, index, result, 1);
- }
-
-
- char *GetDirectoryName(char* buffer, short *index, char *result)
- {
- return GetNextSubstring(buffer, index, result, 1);
- }
-
-
- void GetFileOptions(char *filename)
- {
- #pragma unused (filename)
- short suffixID;
-
- if ((suffixID = FindSuffix(filename)) != kSuffixNotFound)
- gCurrState.currFileOptions = &gAllFileOptions[suffixID];
- else
- // Leave the current suffix rules in place
- gCurrState.currFileOptions = &gDefaultFileOptions;
- }
-
-
- void Push(void)
- {
- // The stack pointer always points to the next available slot
- // Don't push if it would cause a stack overflow
- if (gStackPointer < kStackSize)
- gStateStack[gStackPointer++] = gCurrState;
- }
-
-
- void Pop(short options)
- {
- // Don't pop if it would cause a stack underflow
- if (gStackPointer > 0) {
- CurrentState oldState = gCurrState;
- gCurrState = gStateStack[--gStackPointer];
- if (options & kMergeLabs) {
- // Merge in the active labs from the previous stack state
- short count, limit;
-
- for (count = 0, limit = oldState.numLabs; count < limit; count++) {
- gCurrState.labs[count] = oldState.labs[count];
- }
- gCurrState.numLabs = oldState.numLabs;
- }
- }
- }
-
- void OpenLab(short labID)
- {
- CurrentState *hostFile = &gCurrState;
- short fileID = gStackPointer;
-
- while (hostFile->isInclude) {
- *hostFile = gStateStack[--fileID];
- }
-
- if (gLabs[labID].file == NULL)
- gLabs[labID].file = GetOrMakeFile(hostFile->currFilename, gLabs[labID].directoryName, 0);
- }
-
-
- void CloseLab(short labID)
- {
- if (gLabs[labID].file != NULL)
- fclose(gLabs[labID].file);
- gLabs[labID].file = NULL;
- }
-
-
- void CloseAllLabs(void)
- {
- short labID;
-
- if (gNumLabs > 0) {
- for (labID = 0; labID < gNumLabs; labID++)
- CloseLab(labID);
- }
- }
-
-
- void CopyOrSkip(char *buffer, short paramStart, short paramEnd, char* filename, short activate)
- {
- #pragma unused (paramEnd)
- // Collect the supplied list of labs and activate or deactivate them
- char result[kMaxLineLength];
- short index;
-
- if (gNumLabs == 0) return;
-
- index = paramStart;
- while (GetLabName(buffer, &index, result)) {
- short labID;
-
- labID = FindLab(result);
- if (labID >= 0)
- gLabs[labID].isActive = activate;
- else {
- if (labID == kLabNotFound)
- Error("This lab name hasn't been defined", buffer, index, filename);
- else if (labID == kAllLabs) {
- // Enable or disable all labs
- short count, limit;
- limit = gNumLabs;
-
- for (count = 0; count < limit; count++)
- gLabs[count].isActive = activate;
-
- break; // No use processing any more labs after "all"
- }
- }
- }
- }
-
-
- void ClearFileOption(FileOptions *oneOption)
- // This is called when creating a new file options block. The command character is set to our
- // default ($), everything else is unset.
- {
- short count;
-
- *(oneOption->suffix) = '\0';
- oneOption->numCommentTypes = 0; // Activates // comments until replaced
- strcpy(oneOption->commentDelimiters[0].commentStart, "//");
- *(oneOption->commentDelimiters[0].commentEnd) = '\0';
- for (count = 1; count < kMaxCommentTypes; count++) {
- *(oneOption->commentDelimiters[count].commentStart) = '\0';
- *(oneOption->commentDelimiters[count].commentEnd) = '\0';
- }
- oneOption->commandChar = '$';
- }
-
-
- // ---------------------------------------------------------------------------------
- // Functions which implement LabMaker's commands
- // ---------------------------------------------------------------------------------
-
- void DoInsert(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $insert <lab-name>[ <lab-name>]* data <text to replace this comment with>
- {
- #pragma unused (paramEnd)
- char *dataKeyword;
- short index = paramStart;
- short commentType, commentStart, commentEnd;
- char result[kMaxLineLength];
- char replacement[kMaxLineLength];
- char endOfString[kMaxLineLength];
- char bufferCopy[kMaxLineLength];
-
- // We've been passed the entire buffer which we can modify as desired
- // paramStart points to the start of the comment, paramEnd to the end
- Push();
- gAutoPop = 1;
-
- if (gNumLabs >= 0) {
- // Deactivate all labs
- short count, limit;
- limit = gNumLabs;
-
- for (count = 0; count < limit; count++)
- gLabs[count].isActive = 0;
-
- strcpy(bufferCopy, buffer);
- // Locate the start and end of the comment and the start of the parameters
- FindCommentStart(bufferCopy, &index, &commentStart, &commentType);
- GetNextSubstring(bufferCopy, &index, result, 0); // Skip the command
- SkipWhitespace(bufferCopy, &index);
- paramStart = index;
-
- // Locate the "data" keyword
- dataKeyword = strstr(bufferCopy, "data");
- while (dataKeyword != NULL) {
- char* beforeKeyword, *afterKeyword;
- beforeKeyword =(char*)(dataKeyword - 1);
- afterKeyword =(char*)(dataKeyword + 4);
- // Make sure this word stands alone
- if ((*beforeKeyword != ' ') && (*beforeKeyword != '\t') &&
- (*afterKeyword != ' ') && (*afterKeyword != '\t')) {
- dataKeyword = strstr(afterKeyword, "data");
- continue;
- } else {
- break;
- }
- }
- FindCommentEnd(bufferCopy, &index, &commentEnd, commentType);
-
- // Extract the list of labs and activate each of them
- if (dataKeyword != NULL) {
- *dataKeyword = '\0'; // End the string just after the list of labs
- strcpy(result, (char*)(bufferCopy + (unsigned long)paramStart));
- CopyOrSkip(result, 0, strlen(result), filename, 1);
-
- // Copy out the data portion which will replace our comment
- if (commentEnd < strlen(bufferCopy))
- bufferCopy[commentEnd] = '\0'; // Cut the string at the end of the comment
- strcpy(replacement, &dataKeyword[4]);
- }
- // Remove the comment from the buffer and return the buffer
- // to be distributed
- RemoveComment(buffer, &index, commentStart);
- // Insert the replacement string by splitting the buffer into "before"
- // and "after" strings then concatenating the pieces together.
- strcpy(endOfString, &buffer[index]);
- if (index > 0)
- buffer[index - 1] = '\0';
- strcat(buffer, replacement);
- strcat(buffer, endOfString);
- }
- }
-
-
- void DoComment(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $comment <comment-delim> [<comment-delim>]
- // LabMaker finds the comment delimiters after the
- // directive and uses these to detect comments in the file.
- {
- #pragma unused(paramEnd, filename)
- // Get the first and last tokens from the string, and use these as the
- // comment delimiters
- char result[kMaxLineLength];
- short index = paramStart;
-
- if (gCurrState.currFileOptions->numCommentTypes + 1 <= kMaxCommentTypes) {
- if (GetNextSubstring(buffer, &index, result, 0)) {
- // Make this the starting delimiter
- short currType = gCurrState.currFileOptions->numCommentTypes++;
- strcpy(gCurrState.currFileOptions->commentDelimiters[currType].commentStart, result);
-
- if (GetNextSubstring(buffer, &index, result, 0))
- strcpy(gCurrState.currFileOptions->commentDelimiters[currType].commentEnd, result);
- else
- *gCurrState.currFileOptions->commentDelimiters[currType].commentEnd = '\0';
- }
- }
- }
-
- void DoCreate(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: ($create <lab-name> [in <directory-name>])+ [from <lab-name>]
- // Bind a lab name to a directory name, creating the
- // directory if it doesn't exist, and begin writing
- // the current file to the lab directory. If the "from"
- // keyword is specified, copy from the specified lab
- // into the new lab file before adding any lines.
- {
- char labName[kMaxLabNameLength];
- char keyword[kMaxCommandLength];
- char directory[kMaxLabNameLength];
- int oldLabCount = gNumLabs;
- int dirOK;
- short hasFrom = 0;
- short index = paramStart;
- short indexCopy;
-
- // If we're processing an included file, find the original file
- CurrentState *hostFile = &gCurrState;
- short fileID = gStackPointer;
-
- while (hostFile->isInclude) {
- *hostFile = gStateStack[--fileID];
- }
-
- // Build the list of new labs
- while (index < paramEnd) {
- indexCopy = index;
- if (GetKeyword("from", buffer, &indexCopy, keyword)) {
- hasFrom = 1;
- break; // Exit the loop
- };
-
- if (GetLabName(buffer, &index, labName) == NULL)
- break; // We're out of lab names in the parameter list
-
- // Make sure this lab isn't already represented in our list
- if (FindLab(labName) != kLabNotFound) {
- Error("Duplicate lab name", buffer, index, filename);
- // Skip the folder name for this one lab
- if (GetKeyword("in", buffer, &index, keyword))
- GetDirectoryName(buffer, &index, directory);
- } else {
- // Copy the lab name into our list of labs
- strcpy(gLabs[gNumLabs].labName, labName);
- // See if the user specified a directory name for this lab
- dirOK = 0;
- if (GetKeyword("in", buffer, &index, keyword)) {
- // We have the "in" keyword so extract the directory name
- if (GetDirectoryName(buffer, &index, directory)) {
- strcpy(gLabs[gNumLabs].directoryName, directory);
- dirOK = 1;
- } else {
- Warning("No directory name supplied, using lab name", buffer, index, filename);
- }
- };
- if (!dirOK) {
- // Use the lab name for the directory name
- strcpy(gLabs[gNumLabs].directoryName, labName);
- }
- gNumLabs++; // Another lab added successfully
- }
- }
-
- // Set up the newly added labs
- if (oldLabCount < gNumLabs) {
-
- short count;
- // If there's a "from" keyword, copy the specified source file to the named
- // labs
- if (hasFrom) {
-
- if (GetDirectoryName(buffer, &index, directory)) {
- for (count = oldLabCount; count < gNumLabs; count++) {
- CopyFile(hostFile->currFilename, directory, gLabs[count].directoryName, 0);
- }
- } else {
- Error("No directory name supplied", buffer, index, filename);
- }
- }
-
- // Activate the newly added labs
- for (count = oldLabCount; count < gNumLabs; count++) {
- GetOrMakeDirectory(gLabs[count].directoryName, 0);
- OpenLab(count);
- gLabs[count].isActive = 1;
- }
- }
- }
-
-
- void DoInclude(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $include <file-name>
- // reads in configuration file or another file to be processed
- {
- #pragma unused (paramEnd)
- char includeFile[kFullPathLength];
- short index = paramStart;
-
- if (GetFileName(buffer, &index, includeFile)) {
- Push();
- if (ProcessFile(includeFile, 1) != 0)
- Error("Couldn't open file", buffer, index, filename);
- Pop(kMergeLabs);
- } else
- Error("No file name found", buffer, index, filename);
- }
-
-
- void DoCopy(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $copy <lab-name>[, <lab-name>]*
- // Begins copying lines into the specified labs (including
- // this line if it contains more than just a LabMaker
- // command.
- {
- CopyOrSkip(buffer, paramStart, paramEnd, filename, 1);
- }
-
-
- void DoSkip(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $skip <lab-name>[, <lab-name>]*
- // Stop copying lines (inclusive) into the specified labs.
- {
- #pragma unused (buffer, paramStart, paramEnd, filename)
- CopyOrSkip(buffer, paramStart, paramEnd, filename, 0);
- }
-
-
- void DoPush(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $push
- // Remember the current set of active labs.
- {
- #pragma unused (buffer, paramStart, paramEnd, filename)
- Push();
- }
-
-
- void DoPop(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $pop
- // Recall a pushed set of labs
- {
- #pragma unused (buffer, paramStart, paramEnd, filename)
- Pop(kReplaceLabs);
- }
-
-
- void DoSuffix(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $suffix <filename>|<file-suffix>
- // Beginning of a "suffix definition" block which lists
- // the options for a particular type of file.
- {
- #pragma unused (buffer, paramStart, paramEnd, filename)
- short suffixID;
- short index = paramStart;
- char result[kMaxLineLength];
-
- if (GetFileName(buffer, &index, result)) {
- suffixID = FindSuffix(buffer);
- if (suffixID == kSuffixNotFound) {
- // Add another entry to the file types table
- if (gNumFileOptions + 1 == kMaxFileTypes) { // Don't add more than the limit
- Warning("File options table full", buffer, index, filename);
- return;
- }
- suffixID = gNumFileOptions++;
- gCurrState.currFileOptions = &gAllFileOptions[suffixID];;
- ClearFileOption(gCurrState.currFileOptions);
- strcpy(gCurrState.currFileOptions->suffix, result);
- } else
- // We already know about this suffix, so update its entry in the table
- gCurrState.currFileOptions = &gAllFileOptions[suffixID];
- } else
- Error("File name or suffix not found", buffer, index, filename);
- }
-
-
- void DoDelimiter(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $delimiter <delim-character>
- // Use at the start of a file to change the command prefix
- // character from "$" to something else
- {
- #pragma unused (buffer, paramStart, paramEnd, filename)
- short index = paramStart;
- char result[kMaxLineLength];
-
- if (GetNextSubstring(buffer, &index, result, 0))
- gCurrState.currFileOptions->commandChar = *result;
- else
- Error("Expected a character after 'delimiter'", buffer, index, filename);
- }
-
-
- void DoReset(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $reset
- // Discard the list of labs and reset all parameters
- // to their default settings
- {
- #pragma unused (buffer, paramStart, paramEnd, filename)
- // gNumLabs = 0;
- gNumFileOptions = 0;
- gAutoPop = 0;
- gStackPointer = 0;
- gCurrState.skipBlanks = 0;
- // Set up default values for comment formats, command characters, etc.
- // Our default command character is '$' and we support both C comment formats
- *gDefaultFileOptions.suffix = '\0';
- gDefaultFileOptions.numCommentTypes = 2;
- gDefaultFileOptions.commandChar = '$';
- strcpy(gDefaultFileOptions.commentDelimiters[0].commentStart, "/*");
- strcpy(gDefaultFileOptions.commentDelimiters[0].commentEnd, "*/");
- strcpy(gDefaultFileOptions.commentDelimiters[1].commentStart, "//");
- *(gDefaultFileOptions.commentDelimiters[1].commentEnd) = '\0';
- gCurrFileOptions = &gDefaultFileOptions;
- }
-
-
- void DoOnly(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $only <lab-name>[, <lab-name>]*
- // Only include this line in the specified list of labs.
- {
- Push();
- gAutoPop = 1;
- if (gNumLabs >= 0) {
- short count, limit;
- limit = gNumLabs;
-
- for (count = 0; count < limit; count++)
- gLabs[count].isActive = 0;
- }
- // Activate only the specified labs
- DoCopy(buffer, paramStart, paramEnd, filename);
- }
-
-
- void DoNot(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $not <lab-name>[, <lab-name>]*
- // Exclude just this line from the specified list of labs
- {
- Push();
- gAutoPop = 1;
- // Deactivate only the specified labs
- DoSkip(buffer, paramStart, paramEnd, filename);
- }
-
- void DoEndSuffix(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $end-suffix
- // Resets our comment delimiter and command character to the default
- {
- #pragma unused(buffer, paramStart, paramEnd, filename)
- gCurrState.currFileOptions = &gDefaultFileOptions;
- }
-
-
- void DoSkipBlanks(char *buffer, short paramStart, short paramEnd, char* filename)
- // Usage: $skip-blanks
- // Ignore all blank lines in this file
- {
- #pragma unused(buffer, paramStart, paramEnd, filename)
- gCurrState.skipBlanks = 1;
- }
-
-